using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Input.Touch;


namespace Services
{
    public interface IInputManager
    {
        event InputManager.Tap OnDoubleTap;
        event InputManager.Tap OnTap;
        event InputManager.Tap OnPress;
        event InputManager.Pinch OnPinch;
        event InputManager.Drag OnDrag;
        event InputManager.DragCompleted OnDragCompleted;
        event InputManager.Drag OnHorizontalDrag;
        event InputManager.Drag OnVerticalDrag;
        event InputManager.Flick OnFlick;
    }

    public class InputManager : Microsoft.Xna.Framework.GameComponent, IInputManager
    {
        static readonly float DOUBLE_TAP_INTERVAL = 0.2f;
        static readonly float SINGLE_TAP_INTERVAL = 0.2f;
        static readonly float LONG_TAP_INTERVAL = 0.3f;

        public InputManager(Game game)
            : base(game)
        {
            this.UpdateOrder = (int)ComponentOrders.INPUT;
            game.Services.AddService(typeof(IInputManager), this);
        }

        protected override void Dispose(bool disposing)
        {
            Game.Services.RemoveService(typeof(IInputManager));
            base.Dispose(disposing);
        }

        bool tap_pending = false;
        float time_since_last_release = 10000.0f;
        float time_since_last_press = 10000.0f;
        Vector2 pending_tap_position;

        static bool first_time = true;
        Matrix screen_transform;
        GamePadState last_gps, current_gps;
        public override void Initialize()
        {
            screen_transform = Matrix.CreateScale(Game.GraphicsDevice.Viewport.Width / 480.0f, Game.GraphicsDevice.Viewport.Height / 800.0f, 1.0f);

            if (first_time)
            {
                TouchPanel.EnabledGestures =
                    GestureType.DoubleTap |
                    GestureType.Tap |
                    GestureType.Hold |
                    GestureType.Pinch |
                    GestureType.FreeDrag |
                    GestureType.HorizontalDrag |
                    GestureType.VerticalDrag |
                    GestureType.DragComplete |
                    GestureType.Flick;
                first_time = false;
            }

            last_gps = current_gps = GamePad.GetState(PlayerIndex.One);

            base.Initialize();
        }

        bool mouse_pressed(MouseState mouse_state)
        {
#if WINDOWS || XBOX
            if (mouse_state.LeftButton == ButtonState.Pressed)
                return true;
#endif
            return false;
        }

        bool mouse_released(MouseState mouse_state)
        {
#if WINDOWS || XBOX
            if (mouse_state.LeftButton == ButtonState.Released)
                return true;
#endif
            return false;
        }

        bool skip_next_frame = false;
#if WINDOWS || XBOX
    bool dragging = false;
    MouseState prev_mouse_state = new MouseState();
#endif
        public override void Update(GameTime gameTime)
        {
#if WINDOWS || XBOX
      var curr_mouse_state = Mouse.GetState();
      if (curr_mouse_state.LeftButton == ButtonState.Pressed &&
          prev_mouse_state.LeftButton == ButtonState.Released)
      {
        if (OnTap != null)
          OnTap(new Vector2(curr_mouse_state.X, curr_mouse_state.Y));
      }

      if (curr_mouse_state.RightButton == ButtonState.Pressed &&
          prev_mouse_state.RightButton == ButtonState.Released)
      {
        if (OnDoubleTap != null)
          OnDoubleTap(new Vector2(curr_mouse_state.X, curr_mouse_state.Y));
      }

      if (Keyboard.GetState().IsKeyDown(Keys.Space))
      {
        if (OnPress != null)
          OnPress(new Vector2(curr_mouse_state.X, curr_mouse_state.Y));
      }

      //emulazione pinch
      if (curr_mouse_state.RightButton == ButtonState.Pressed && Keyboard.GetState().IsKeyDown(Keys.LeftControl))
      {
        if (OnPinch != null)
          OnPinch(new Vector2(curr_mouse_state.X, curr_mouse_state.Y), Vector2.Zero, new Vector2(curr_mouse_state.X - prev_mouse_state.X, curr_mouse_state.Y - prev_mouse_state.Y), Vector2.Zero);
      }

      //emulazione drag
      if (curr_mouse_state.LeftButton == ButtonState.Pressed && Keyboard.GetState().IsKeyDown(Keys.LeftControl))
      {
        if (OnDrag != null)
          OnDrag(new Vector2(curr_mouse_state.X, curr_mouse_state.Y));
        dragging = true;
      }
      //emulazione dragcompleted
      if (curr_mouse_state.LeftButton == ButtonState.Released && dragging)
      {
        dragging = false;
        if (OnDragCompleted != null)
          OnDragCompleted();
      }

      prev_mouse_state = curr_mouse_state;
#endif

            last_gps = current_gps;
            current_gps = GamePad.GetState(PlayerIndex.One);

            while (TouchPanel.IsGestureAvailable)
            {
                GestureSample gs = TouchPanel.ReadGesture();

                switch (gs.GestureType)
                {
                    case GestureType.Tap:
                        if (OnTap != null)
                            OnTap(gs.Position);
                        break;
                    case GestureType.DoubleTap:
                        if (OnDoubleTap != null)
                            OnDoubleTap(gs.Position);
                        break;
                    case GestureType.Hold:
                        if (OnPress != null)
                            OnPress(gs.Position);
                        break;
                    case GestureType.Pinch:
                        if (OnPinch != null)
                            OnPinch(gs.Position, gs.Position2, gs.Delta, gs.Delta2);
                        break;
                    case GestureType.HorizontalDrag:
                        if (OnHorizontalDrag != null)
                            OnHorizontalDrag(gs.Position);
                        break;
                    case GestureType.VerticalDrag:
                        if (OnVerticalDrag != null)
                            OnVerticalDrag(gs.Position);
                        break;
                    case GestureType.FreeDrag:
                        if (OnDrag != null)
                            OnDrag(gs.Position);
                        break;
                    case GestureType.DragComplete:
                        if (OnDragCompleted != null)
                            OnDragCompleted();
                        break;
                    case GestureType.Flick:
                        if (OnFlick != null)
                            OnFlick(gs.Delta);
                        break;
                }
            }

            if (IsBackButtonPressed() && OnTap != null) OnTap(Vector2.One * (-1));
        }

        public bool IsBackButtonPressed()
        {
            return current_gps.IsButtonDown(Buttons.Back) && last_gps.IsButtonUp(Buttons.Back);
        }

        public delegate void Tap(Vector2 tap_position);
        public delegate void Pinch(Vector2 position1, Vector2 position2, Vector2 delta1, Vector2 delta2);
        public delegate void Drag(Vector2 position);
        public delegate void DragCompleted();
        public delegate void Flick(Vector2 delta);
        public event Tap OnTap;
        public event Tap OnDoubleTap;
        public event Tap OnPress;
        public event Pinch OnPinch;
        public event Drag OnDrag;
        public event Drag OnHorizontalDrag;
        public event Drag OnVerticalDrag;
        public event DragCompleted OnDragCompleted;
        public event Flick OnFlick;
    }
}
